<script setup>
import useChannel from '@/store/channel'
import { useEventListener } from '@vueuse/core/index'
import useUser from '@/store/user'
import { addMsgTextTag, forwardMessage, getMsgRecallConfig } from '@/assets/api/message'
import { redirect, removeChannel } from '@/utils/dialog-utils'
import { useInfiniteScroll, useScroll } from '@vueuse/core'
import dayjs from 'dayjs'
import { useRouter } from 'vue-router'
import useEventBus from '@/use/use-event-bus'
import { ElMessage } from 'element-plus'
import { DArrowRight } from '@element-plus/icons-vue'

const props = defineProps({
  remindFlag: {
    type: Boolean,
    default: false,
  },
  disabledUnreadJump: {
    type: Boolean,
    default: false,
  },
  // 开启回到最新消息
  canBackLatest: {
    type: Boolean,
    default: false,
  },
  messageList: {
    type: Array,
    default: () => [],
  },
  // 禁用悬浮操作
  disabledOperation: {
    type: Boolean,
    default: false,
  },
  // 禁用时间信息
  disabledDate: {
    type: Boolean,
    default: false,
  },
  disabledFooter: {
    type: Boolean,
    default: false,
  },
  viewImg: Boolean,
  canLoadMore: Function,
  disabledContextmenu: {
    type: [Boolean, Array],
    default: false,
  },
})

const emit = defineEmits(['loadBefore', 'loadAfter', 'loadMore', 'loadLatest'])

defineExpose({
  scrollBottom,
  jumpMessage,
})
let mentionMsg = null

const eventBus = useEventBus()
eventBus.on('dialogScrollBottom', () => {
  scrollBottom(false, true)
})

const channelStore = useChannel()
const contextmenuShow = ref(false)
const contextmenuPosition = ref({
  left: 0,
  top: 0,
})
const contextmenuMessage = ref(null)
const scrollBodyRef = ref()
const router = useRouter()
const multipleChoice = ref(false)
const isRecall = ref(true)
const recallConfig = ref()
const { y: scrollTop, arrivedState } = useScroll(scrollBodyRef)

watch(
  () => channelStore.dialog,
  ({ removeFlag, deleteFlag }) => {
    if (removeFlag) {
      removeChannel(channelStore.dialog.id, {
        content: `你已被移出${channelStore.dialog.name}`,
        title: '移出群聊',
      }).then(() => {
        if (channelStore.channelList.length > 0) {
          channelStore.setDialog(channelStore.channelList[0])
        } else {
          channelStore.setDialog(null)
        }
        router.push(`/chat${redirect()}`)
      })
    }
    if (channelStore.channelManager?.userId !== userStore.userInfo.userId && deleteFlag) {
      removeChannel(channelStore.dialog.id, {
        content: '该群聊已解散',
        title: '群聊解散',
      }).then(() => {
        if (channelStore.channelList > 0) {
          channelStore.setDialog(channelStore.channelList[0])
        } else {
          channelStore.setDialog(null)
        }
        router.push(`/chat${redirect()}`)
      })
    }
  },
  {
    deep: true,
  }
)

const channelSelectRef = ref()

const contextmenuRef = ref()

/**
 * 右键菜单
 * @param {Object} data
 * {
 *   message {Object} 消息
 *   show {Boolean} 展示
 *   position {Object} 位置
 * }
 */
function onContextmenu(data) {
  contextmenuMessage.value = data.message
  contextmenuShow.value = data.show
  nextTick(() => {
    let left = data.position.left
    let top = data.position.top
    const { width, height } = contextmenuRef.value.el.getBoundingClientRect()
    if (left + width > document.body.clientWidth) {
      left = left - width
    }
    if (top + height > document.body.clientHeight) {
      top = top - height
    }
    contextmenuPosition.value = { left: `${left}px`, top: `${top}px` }
  })

  // 根据时间判断是否显示撤回按钮
  const currentTime = dayjs().valueOf()
  const createTime = dayjs(data.message.createTime).valueOf()
  const limit = data.message.files ? recallConfig.value.fileMsgLimit : recallConfig.value.textMsgLimit
  const specifiedTime = createTime + limit * 60 * 1000
  if (currentTime > specifiedTime) {
    isRecall.value = false
  }
}

const userStore = useUser()

let oldScrollHeight = 0

if (props.canLoadMore) {
  useInfiniteScroll(
    scrollBodyRef,
    (state) => {
      if (state.isScrolling) {
        emit('loadAfter')
        emit('loadMore', 'bottom')
      }
    },
    {
      interval: 500,
      canLoadMore: () => props.canLoadMore('bottom'),
    }
  )
  useInfiniteScroll(
    scrollBodyRef,
    (state) => {
      if (state.isScrolling) {
        emit('loadBefore')
        emit('loadMore', 'top')
      }
    },
    {
      interval: 500,
      direction: 'top',
      canLoadMore: () => props.canLoadMore('top'),
    }
  )
}

getMessageRecallConfig()

// 查询撤回消息配置
function getMessageRecallConfig() {
  getMsgRecallConfig().then((res) => {
    if (res.code === 200) {
      recallConfig.value = res.data
    }
  })
}

/**
 * 滚动到底部
 */
function scrollBottom(force = false, hasNext = false) {
  nextTick(() => {
    if (scrollBodyRef.value) {
      const bottomFlag = Math.abs(scrollTop.value) + scrollBodyRef.value.clientHeight >= oldScrollHeight - 50
      if (force) {
        scrollBodyRef.value.scrollTop = scrollBodyRef.value.scrollHeight
      } else if (arrivedState.top && oldScrollHeight) {
        scrollBodyRef.value.scrollTop = scrollBodyRef.value.scrollHeight - oldScrollHeight
      } else if (bottomFlag && hasNext) {
        scrollBodyRef.value.scrollTop = scrollBodyRef.value.scrollHeight
      } else {
        scrollBodyRef.value.scrollTop = scrollTop.value
      }
      oldScrollHeight = scrollBodyRef.value.scrollHeight
    }
  })
}

function handleBodyScroll() {
  contextmenuShow.value = false
}

function createTimeDivisionText(time) {
  const date = dayjs(time)
  if (date.isToday()) {
    return `今天 ${date.format('HH:mm:ss')}`
  }
  if (date.isYesterday()) {
    return `昨天 ${date.format('HH:mm:ss')}`
  }
  return date.format('YYYY年MM月DD日 HH:mm:ss')
}

const backLatest = computed(() => {
  if (!channelStore.dialog) return false
  const { lastMessage } = channelStore.dialog
  if (!lastMessage || !props.messageList.length) return false
  return props.messageList.every((i) => i.id !== lastMessage.id)
})

// 未读消息
const unreadCount = computed(() => {
  const count = props.messageList.filter((i) => !i.metaData.status.readFlag).length
  return count > 99 ? `99+` : count
})

// 点击新消息定位
function handleUpdateUnread() {
  const data = props.messageList.filter((i) => !i.metaData.status.readFlag)
  if (data.length) {
    router.replace(`/chat?channelId=${channelStore.channelId}&jumpId=${data[0].id}&type=message`)
  }
}

// 跳转到消息
function jumpMessage(messageId) {
  nextTick(() => {
    const messageEl = getMessageBoxDom(messageId)
    messageEl?.scrollIntoView({ behavior: 'auto', block: 'end' })
  })
}

// 跳转@我的消息
function jumpMention() {
  mentionMsg = channelStore.mentionMeMsgList[channelStore.mentionMeMsgList.length - 1]
  channelStore.removeMentionMeMsg(mentionMsg.msgId)
  router.replace(`/chat?channelId=${channelStore.channelId}&jumpId=${mentionMsg.msgId}&type=message`)
}

function getMessageBoxDom(id) {
  return scrollBodyRef.value?.querySelector(`.message-box-user[message-id="${id}"]`)
}

const forwardMsgList = ref([])

const forwardType = ref(2)

// 转发消息
function handleForwardMessage(content, channelList) {
  // 单条转发默认是合并转发
  const forwardMsgIds = multipleChoice.value ? forwardMsgList.value.map((i) => i.id) : [contextmenuMessage.value.id]
  forwardMessage({
    forwardMsgIds,
    content,
    channelIds: channelList.map((i) => {
      return {
        dataId: i.id,
        dataType: i.dataType,
      }
    }),
    forwardType: forwardType.value,
  }).then((res) => {
    if (res.code === 200) {
      ElMessage.success('转发成功')
      multipleChoice.value = false
      forwardType.value = 2
      forwardMsgList.value = []
    } else {
      ElMessage.error(res.msg)
    }
  })
}

function handleMessageCheck(message, check) {
  if (check) {
    forwardMsgList.value.push(message)
  } else {
    const index = forwardMsgList.value.findIndex((i) => i.id === message.id)
    forwardMsgList.value.splice(index, 1)
  }
}

function deselectMultiple() {
  multipleChoice.value = false
  forwardMsgList.value = []
}

function openChannelSelect(value) {
  forwardType.value = value
  channelSelectRef.value.open()
}

const urlList = ref([])
const viewImgVisible = ref(false)
const initialIndex = ref(0)

if (props.viewImg) {
  eventBus.on('setUrlList', (url) => {
    urlList.value.push(url)
  })

  eventBus.on('viewImg', (url) => {
    initialIndex.value = urlList.value.findIndex((i) => i === url) || 0
    if (initialIndex.value !== -1) {
      viewImgVisible.value = true
    }
  })
}

eventBus.on('openMsgCustomTag', () => {
  addTagVisible.value = true
})

eventBus.on('addMsgCustomTag', handleAddTag)

const addTagVisible = ref(false)

const formRef = ref()

const rules = {
  tag: [{ required: true, message: '标签至少一个字符', trigger: 'blur' }],
}

const tagForm = ref({
  tag: '',
  color: '--elp-color-primary',
})

const colorIndex = ref(0)

const colors = [
  '--elp-color-primary',
  '--elp-color-success',
  '--elp-color-warning',
  '--elp-color-danger',
  '--elp-color-error',
  '--elp-color-info',
]

// 改变标签颜色
function changeTagColor() {
  colorIndex.value++
  if (colorIndex.value === colors.length) {
    colorIndex.value = 0
  }
  tagForm.value.color = colors[colorIndex.value]
}

// 添加标签
function handleAddTag(msgId, item) {
  if (item) {
    tagForm.value = item
  }
  addMsgTextTag(msgId, tagForm.value).then(() => {
    addTagVisible.value = false
    clearTag()
  })
}

// 清空颜色
function clearTag() {
  tagForm.value = {
    tag: '',
    color: '--elp-color-primary',
  }
}

onMounted(() => {
  scrollBottom(true)
  useEventListener('click', () => {
    contextmenuShow.value = false
  })
})
</script>

<template>
  <div class="chat-content-body">
    <!--消息提醒-->
    <template v-if="!props.remindFlag">
      <div
        v-if="channelStore.mentionMeMsgList.filter((item) => item.topicFlag === props.remindFlag).length"
        class="jump-mention suspension"
        @click="jumpMention">
        <el-icon><DArrowRight></DArrowRight></el-icon>
        有人@你
      </div>
      <div v-if="props.canBackLatest && backLatest" class="back-latest suspension" @click="emit('loadLatest')">
        <el-icon><DArrowRight></DArrowRight></el-icon>
        回到最新消息
      </div>
      <div
        v-if="!props.disabledUnreadJump && !backLatest && unreadCount > 0"
        class="back-latest suspension"
        @click="handleUpdateUnread">
        <el-icon><DArrowRight></DArrowRight></el-icon>
        {{ unreadCount }} 条新消息
      </div>
    </template>

    <!--消息列表-->
    <div ref="scrollBodyRef" class="chat-content-scroll" @scroll="handleBodyScroll">
      <template v-for="(item, index) in props.messageList" :key="item.id">
        <div
          class="time-division"
          v-if="!index || dayjs(item.createTime).date() !== dayjs(props.messageList[index - 1].createTime).date()">
          {{ createTimeDivisionText(item.createTime) }}
        </div>
        <MessageBox
          :message="item"
          :show-check-box="multipleChoice"
          :disabled-operation="props.disabledOperation"
          :disabled-footer="props.disabledFooter"
          :disabled-date="props.disabledDate"
          :checked="forwardMsgList.map((i) => i.id).includes(item.id)"
          @check="handleMessageCheck"
          @openContextmenu="
            ;(!props.disabledContextmenu || disabledContextmenu.length) && onContextmenu($event)
          "></MessageBox>
        <div v-if="multipleChoice" class="multiple-operation">
          <el-button type="primary" round @click="deselectMultiple">退出多选</el-button>
          <el-button type="primary" round @click="openChannelSelect(1)">逐条转发</el-button>
          <el-button type="primary" round @click="openChannelSelect(2)">合并转发</el-button>
        </div>
      </template>
    </div>

    <!--右键菜单-->
    <MsgContextmenu
      ref="contextmenuRef"
      v-model="contextmenuShow"
      :disabled-contextmenu="props.disabledContextmenu"
      :message="contextmenuMessage"
      :position="contextmenuPosition"
      :isRecall="isRecall"
      @forward="channelSelectRef.open()"
      @multipleChoice="multipleChoice = true"
      @openPinMsgDialog="emit('openPinMsgDialog', $event)"></MsgContextmenu>
    <ChannelSelect ref="channelSelectRef" @submit="handleForwardMessage"></ChannelSelect>

    <!--图片预览-->
    <el-image-viewer
      v-if="props.viewImg && viewImgVisible"
      :url-list="urlList"
      :initial-index="initialIndex"
      @close="viewImgVisible = false"></el-image-viewer>

    <!--添加标签-->
    <TaskChatDialog v-model="addTagVisible" title="添加自定义标签" append-to-body width="300px" :show-close="false">
      <el-form ref="formRef" :model="tagForm" :rules="rules">
        <el-form-item prop="tag">
          <el-input v-model="tagForm.tag" placeholder="请输入标签(最多四个字符)" style="flex: 1"></el-input>
          <div
            @click="changeTagColor"
            style="width: 32px; height: 32px; border-radius: 4px; cursor: pointer; margin-left: 3px"
            :style="{ backgroundColor: `var(${colors[colorIndex]})` }"></div>
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click.stop="addTagVisible = false">取消</el-button>
        <el-button type="primary" @click="() => handleAddTag()">确定</el-button>
      </template>
    </TaskChatDialog>
  </div>
</template>

<style scoped lang="scss">
.chat-content-body {
  position: relative;
  height: 100%;
  overflow: hidden auto;
  background-color: var(--elp-bg-color);
  .chat-content-scroll {
    padding: 0 16px;
    height: 100%;
    overflow: hidden auto;
    .time-division {
      font-size: 13px;
      padding: 10px;
      text-align: center;
    }
  }
  .suspension {
    cursor: pointer;
    position: absolute;
    display: inline-block;
    padding: 10px 15px;
    border-radius: 32px;
    right: 8px;
    background-color: var(--elp-bg-color);
    box-shadow: var(--elp-box-shadow);
    user-select: none;
    z-index: 1;
  }
  .jump-mention {
    color: var(--elp-color-danger);
    top: 77px;
    .elp-icon {
      transform: rotateZ(-90deg);
    }
  }
  .back-latest {
    color: var(--elp-color-primary-light-3);
    bottom: 10px;
    .elp-icon {
      transform: rotateZ(90deg);
    }
  }
  .multiple-operation {
    display: flex;
    position: absolute;
    bottom: 10px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 2;
  }
}
</style>
<style lang="scss">
.message-box-contextmenu {
  position: absolute;
  font-size: 14px;
  padding: 10px;
  width: 200px;
  border-radius: 5px;
  background-color: var(--elp-bg-color);
  box-shadow: var(--elp-box-shadow);
  z-index: 1;
  .menu-item {
    cursor: pointer;
    padding: 5px 10px;
    border-radius: 5px;
    &:hover {
      background-color: var(--elp-fill-color-light);
    }
  }
  .elp-divider--horizontal {
    margin: 5px 0;
  }
}
</style>
